package xdb;

import com.goldhuman.Common.Marshal.OctetsStream;
import xdb.util.AutoKeys;

import java.sql.SQLException;

public final class TableSys extends Table {
    // open 以后这几个变量本身只有读取操作，不需要锁保护。
    private StorageSys storage;
    private AutoKeys autoKeys;

    public AutoKeys getAutoKeys() {
        return autoKeys;
    }

    //////////////////////////////////////////////////////////////////////////////////
    @Override
    public String getName() {
        return "_sys_";
    }

    @Override
    public Persistence getPersistence() {
        return Persistence.DB;
    }

    @Override
    public void clean() {
    }

    @Override
    Storage open(XdbConf xconf) {
        if (null != storage) {
            throw new XError("TableSys has opened : " + getName());
        }

        TableConf tconf = xconf.getTableConf(getName());
        if (null == tconf) {
            tconf = new TableConf(getName(), 1, 1);
        }

        storage = new StorageSys(xconf, tconf);

        // 装载系统表数据记录
        try {
            autoKeys = storage.loadAutoKeys(xconf, tconf);
        } catch (SQLException e) {
            throw new XError("TableSys loadAutoKeys SQLException:" + getName());
        }

        // 保存在storage.flush里面实现，在Checkpoint.flush操作统一刷新。
        //Trace.debug("load:" + autoKeys.toString());
        return storage;
    }

    @Override
    void close() {
        if (null != storage) {
            storage = null;
        }
    }

    private final class StorageSys extends Storage {
        private OctetsStream keyOfAutoKeys = null; // 记住key，防止配置修改，也有点优化作用。
        /**
         * 并发
         * 1 autoKey 的所有访问都在内存中，snapshotValue 仅在 Checkpoint 线程里面访问。
         * 2 flush步骤里面，不需要拦截对 storage 的访问。see TStorage & TRecord。
         * 3 marshalN, marshal0, snapshot 和 flush 不会并发，不并发保护。
         */
        private OctetsStream snapshotValue = null;

        StorageSys(XdbConf xconf, TableConf tconf) {
            super(xconf, tconf);
        }

        AutoKeys loadAutoKeys(XdbConf xconf, TableConf tconf) throws SQLException {
            int localId = xdb.Xdb.getInstance().getConf().getLocalId();
            if (localId <= 0) {
                return null;
            }

            keyOfAutoKeys = new OctetsStream().marshal(localId);
            OctetsStream valueOfAutoKeys = super.find(keyOfAutoKeys);
            return new AutoKeys(valueOfAutoKeys, localId, false);
        }

        @Override
        int marshalN() {
            if (null != TableSys.this.autoKeys && TableSys.this.autoKeys.isDirty()) {
                snapshotValue = TableSys.this.autoKeys.encodeValue(-1922908697795782568L);
                return 1;
            }
            return 0;
        }

        @Override
        int marshal0() {
            return 0;
        }

        @Override
        int snapshot() {
            return null != snapshotValue ? 1 : 0;
        }

        @Override
        int flush(long flushTime) {
            if (null != snapshotValue) {
                super.replaceUnsafe(keyOfAutoKeys, snapshotValue, flushTime);
                snapshotValue = null;
                return 1;
            }
            return 0;
        }

    }

}
